#pragma once

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <QTimer>

#define EGL_EGLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
#include "decoder.h"

#include <boost/optional.hpp>

struct FRAME_BUFFER
{
  FRAME_BUFFER(const GLuint frame, const GLuint texture, const GLsizei width, const GLsizei height)
    : frame_(frame)
    , texture_(texture)
    , width_(width)
    , height_(height)
  {
  }

  ~FRAME_BUFFER()
  {
    glDeleteFramebuffers(1, &frame_);
    glDeleteTextures(1, &texture_);
  }

  GLuint frame_;
  GLuint texture_;
  GLsizei width_;
  GLsizei height_;

};
struct EGL_FRAME
{
  EGL_FRAME(const EGLImageKHR image, const MppFrameColorSpace colour_space, const MppFrameColorRange colour_range, const RK_U32 width, const RK_U32 height)
    : image_(image)
    , colour_space_(colour_space)
    , colour_range_(colour_range)
    , width_(width)
    , height_(height)
  {
  }

  EGLImageKHR image_;
  MppFrameColorSpace colour_space_;
  MppFrameColorRange colour_range_;
  RK_U32 width_;
  RK_U32 height_;

};

class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT

public:
    MyOpenGLWidget(QWidget *parent = nullptr);
    ~MyOpenGLWidget();

    void set_mp4_path(const std::string& path);
    void start();

private Q_SLOTS:
    void on_timeout();

private:
    void init_egl_functions();
    void do_egl_image(const FrameStruct& frame);
    void destroy_egl_frames();

    // QOpenGLWidget interface
protected:
    void initializeGL() override;
    void resizeGL(int w, int h) override;
    void paintGL() override;

private:
    QOpenGLShaderProgram* m_program = nullptr;
    QOpenGLShaderProgram* m_oes_program = nullptr;

    QOpenGLVertexArrayObject* m_vao = nullptr;
    QOpenGLBuffer* m_vbo = nullptr;
    QOpenGLBuffer* m_ebo = nullptr;

    PFNEGLCREATEIMAGEKHRPROC egl_create_image_khr;
    PFNEGLDESTROYIMAGEKHRPROC egl_destroy_image_khr;
    PFNEGLCREATESYNCKHRPROC egl_create_sync_khr;
    PFNEGLDESTROYSYNCKHRPROC egl_destroy_sync_khr;
    PFNEGLCLIENTWAITSYNCKHRPROC egl_client_wait_sync_khr;
    PFNGLEGLIMAGETARGETTEXTURE2DOESPROC gl_egl_image_target_texture_2_does;

    int m_oes_texture_sampler_location;
    int m_texture_sampler_location;

    Decoder* m_decoder;
    std::map<MppBuffer, EGL_FRAME> m_egl_images;
    int m_egl_colour_space_override_index = 1;
    int m_egl_colour_range_override_index = 1;
    std::unique_ptr<FRAME_BUFFER> m_frame_buffer;
    boost::optional<MppFrameColorSpace> m_mpp_colour_space;
    boost::optional<MppFrameColorRange> m_mpp_colour_range;
    boost::optional<MppFrameColorPrimaries> m_mpp_colour_primaries;

    EGLDisplay m_egl_display;

    QTimer m_timer;
    bool m_flag = false;
    FrameStruct m_frame;
};
